/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef CORE_ACSREQUEST_H_
#define CORE_ACSREQUEST_H_

#include <map>
#include <string>
#include <typeinfo>

#include "auth/ISigner.h"
#include "auth/Credential.h"
#include "auth/AcsURLEncoder.h"
#include "auth/ISignatureComposer.h"
#include "http/FormatType.h"
#include "http/HttpRequest.h"
#include "http/ProtocolType.h"
#include "regions/ProductDomain.h"

namespace aliyun {

// to limit too many template functions
class AcsRequestBase : public http::HttpRequest {
 public:
  typedef std::string string;
  typedef http::ProtocolType ProtocolType;

  virtual ~AcsRequestBase() {
  }

  AcsRequestBase(string product) {
    putHeaderParameter("x-sdk-client", "C++/1.0.0");
    product_ = product;
    composer_ = NULL;
    protocol_ = ProtocolType::HTTP;
  }

  AcsRequestBase(string product, string version) {
    product_ = product;
    version_ = version;
    composer_ = NULL;
    protocol_ = ProtocolType::HTTP;
  }

  // getter and setters
  const string& getActionName() const {
    return actionName_;
  }

  void setActionName(const string& actionName) {
    actionName_ = actionName;
  }

  const string& getProduct() const {
    return product_;
  }

  http::ProtocolType getProtocol() const {
    return protocol_;
  }

  void setProtocol(http::ProtocolType protocol) {
    protocol_ = protocol;
  }

  const std::map<string, string>& getDomainParameters() const {
    return domainParameters_;
  }

  const std::map<string, string>& getQueryParameters() const {
    return queryParameters_;
  }

  void putQueryParameter(string name, string value) {
    queryParameters_[name] = value;
  }

  void putDomainParameter(string name, string value) {
    domainParameters_[name] = value;
  }

  http::FormatType getAcceptFormat() const {
    return acceptFormat_;
  }

  void setAcceptFormat(http::FormatType acceptFormat) {
    acceptFormat_ = acceptFormat;
    putHeaderParameter("Accept",
                       http::FormatType::mapFormatToAccept(acceptFormat));
  }

  const string& getRegionId() const {
    return regionId_;
  }

  void setRegionId(const string& regionId) {
    regionId_ = regionId;
  }

  const string& getSecurityToken() const {
    return securityToken_;
  }

  void setSecurityToken(const string& securityToken) {
    securityToken_ = securityToken;
  }

  const string& getVersion() const {
    return version_;
  }

  void setVersion(const string& version) {
    version_ = version;
  }

  static string concatQueryString(const std::map<string, string>& parameters) {
    if (parameters.size() == 0)
      return "";
    string url;
    for (std::map<string, string>::const_iterator it = parameters.begin();
        it != parameters.end(); ++it) {
      url += auth::AcsURLEncoder::encode(it->first);
      string val = it->second;
      if (val.length() != 0) {
        url += "=";
        url += auth::AcsURLEncoder::encode(val);
      }
      url += "&";
    }

    if (parameters.size() > 0) {
      url.resize(url.length() - 1);
    }
    return url;
  }

  virtual http::HttpRequest& signRequest(
      auth::ISigner* signer, const auth::Credential& credential,
      const http::FormatType format, const regions::ProductDomain& domain) = 0;

  virtual string compsoeUrl(string endpoint,
                            const std::map<string, string>& queries) = 0;

 protected:
  auth::ISignatureComposer* composer_;

 private:
  string version_;
  string product_;
  string actionName_;
  string regionId_;
  string securityToken_;
  http::FormatType acceptFormat_;
  http::ProtocolType protocol_;
  std::map<string, string> queryParameters_;
  std::map<string, string> domainParameters_;
};

// AcsRequestBase is a middle layer, for optimize code size
template<class T>
class AcsRequest : public AcsRequestBase {
 public:
  typedef T ResponseType;

  AcsRequest(string product)
      : AcsRequestBase(product) {
  }

  AcsRequest(string product, string version)
      : AcsRequestBase(product, version) {
  }
};

}  // namespace aliyun

#endif  // CORE_ACSREQUEST_H_
